Skip to content

Add callable and conditional parallel branches (0075)#175

Merged
chris-colinsky merged 3 commits into
mainfrom
feature/0075-parallel-branches-callable-branches
Jun 21, 2026
Merged

Add callable and conditional parallel branches (0075)#175
chris-colinsky merged 3 commits into
mainfrom
feature/0075-parallel-branches-callable-branches

Conversation

@chris-colinsky

Copy link
Copy Markdown
Member

Implements proposal 0075 (spec v0.66.0): inline-callable parallel branches and conditional when, plus the callable-branch observability the spec later pinned in fixture 110. Advances the pin v0.65.0 to v0.66.1.

Callable branches and when

A BranchSpec's work may now be given as call (an inline async function over the parent state returning a parent-shaped partial update) instead of a compiled subgraph. The contribution is the returned partial directly, merged via the parent reducer with no projection. This makes the primitive adoptable for the "M heterogeneous lightweight parallel calls over shared state" shape (hybrid recall, paired reads) that previously dropped to a hand-rolled concurrent gather, while reusing the existing concurrency, fail-fast cancellation, per-branch failure isolation, and reducer fan-in.

A branch gives its work as exactly one of subgraph / call, and a callable branch declares no inputs / outputs, else the new compile-time ParallelBranchesInvalidBranchSpec. A node may mix both forms freely.

Any branch may carry an optional when predicate over the parent state, evaluated once at dispatch. A False result skips the branch entirely: no dispatch, contribution, observer events, or span. All-branches-skipped is a valid no-op.

Observability

A callable branch is the unit of work: it emits one started/completed observer pair keyed by branch_name, which both bundled observers render as a single per-branch dispatch span (OTel) / observation (Langfuse) with no inner-node span beneath it. A when-skipped branch emits nothing. This matches spec conformance fixture 110 (v0.70.1), which pins the callable-branch span shape; the behavior is implemented here and the fixture itself wires when the pin reaches v0.70.1 (guarded meanwhile by a mirror unit test).

The NODE event's branch_count is now the number of branches that dispatch (when-skipped excluded), while branch_names stays the full declared set.

Also fixes a pre-existing duplicate Langfuse observation for parallel-branches and fan-out nodes: those emit their own NODE observation, so synthesizing a subgraph-wrapper observation at the same namespace duplicated it. The OTel observer already guarded against this; the guard is now mirrored in Langfuse.

Conformance and docs

Pins the spec submodule to v0.66.1 (0075 at v0.66.0 plus the v0.66.1 call-level-retry Langfuse-mapping clarification, which the observer already satisfies). Wires conformance fixtures 073-075. The parallel-branches concept page documents both branch forms and when, and the example gains a pure inline reading-time callable branch and a when-gated translation branch.

A BranchSpec's work may now be given as `call` (an inline async function
over the parent state returning a parent-shaped partial) instead of a
compiled `subgraph`, and any branch may carry a `when` predicate that
skips it at dispatch. `subgraph` becomes optional; exactly one of
subgraph/call per branch and no inputs/outputs on a callable branch,
else the new compile-time ParallelBranchesInvalidBranchSpec. A node may
mix both forms. All-branches-skipped is a valid no-op.

A callable branch is the unit of work: it emits one started/completed
observer pair keyed by branch_name at the parallel-branches node's own
namespace, which both bundled observers render as a single per-branch
dispatch span (OTel) / observation (Langfuse) with no inner-node span; a
when-skipped branch emits nothing. The NODE event's branch_count is the
number of branches that dispatch (when-skipped excluded), while
branch_names stays the full declared set.

Also fix a pre-existing duplicate Langfuse observation for
parallel-branches and fan-out nodes: those emit their own NODE
observation, so synthesizing a subgraph-wrapper observation at the same
namespace duplicated it. Mirror the OTel observer's existing guard so
inner work parents under the single NODE observation.
Advance the spec submodule, __spec_version__, pyproject spec_version,
and conformance.toml spec_pin to v0.66.1. This absorbs 0075 (v0.66.0)
and the v0.66.1 patch (a call-level-retry Langfuse-mapping clarification
the observer already satisfies, no code change). Mark proposal 0075
implemented and regenerate the bundled AGENTS.md.

Wire conformance fixtures 073-075: the adapter parses the `call` and
`when` branch directives, the fixture schema makes `subgraph` optional,
and the pipeline-utilities driver registers the three fixtures.

Document callable branches and conditional `when` in the parallel-
branches concept page, and extend the example with a pure inline
reading-time callable branch and a `when`-gated translation branch so it
covers both branch forms.
Copilot AI review requested due to automatic review settings June 20, 2026 23:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements spec proposal 0075 by extending ParallelBranchesNode to support inline-callable branches (BranchSpec(call=...)) and optional conditional dispatch (when=...), along with observer updates (OTel + Langfuse) and a spec pin bump to v0.66.1.

Changes:

  • Add callable-branch and when support to parallel branches, including compile-time validation (ParallelBranchesInvalidBranchSpec) and updated branch_count semantics.
  • Update OTel/Langfuse observers to correctly render callable branches and avoid duplicate Langfuse observations for parallel-branches/fan-out nodes.
  • Update conformance wiring, unit tests, docs, examples, and spec version pin references.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/openarmature/graph/parallel_branches.py Implements callable branches + conditional dispatch in the runtime node.
src/openarmature/graph/compiled.py Updates NODE event config (branch_count) to exclude when-skipped branches.
src/openarmature/graph/builder.py Adds compile-time validation for subgraph vs call and projection rules.
src/openarmature/graph/errors.py Introduces ParallelBranchesInvalidBranchSpec compile error.
src/openarmature/graph/__init__.py Exports the new compile error symbol.
src/openarmature/observability/otel/observer.py Adds callable-branch span handling for OTel.
src/openarmature/observability/langfuse/observer.py Fixes duplicate observation and parents callable branches correctly.
tests/unit/test_parallel_branches.py Adds unit tests for callable branches, when, and event shapes.
tests/unit/test_observability_otel.py Adds OTel span-shape test for callable branches + when skip.
tests/unit/test_observability_langfuse.py Adds regression test for duplicate parallel-branches observations.
tests/conformance/adapter.py Extends conformance adapter to translate call and when directives.
tests/conformance/harness/directives.py Updates directive model to support call/when.
tests/conformance/test_pipeline_utilities.py Wires fixtures 073–075 into the runner selection.
docs/concepts/parallel-branches.md Documents callable branches and conditional when.
docs/examples/parallel-branches.md Updates example docs to cover new branch forms.
examples/parallel-branches/main.py Updates runnable example to include a callable branch and when-gated branch.
tests/test_smoke.py Updates spec version assertion to v0.66.1.
src/openarmature/__init__.py Updates __spec_version__ to v0.66.1.
pyproject.toml Updates tool.openarmature.spec_version to v0.66.1.
src/openarmature/AGENTS.md Updates bundled agent-guide spec version references.
conformance.toml Advances spec pin and records proposal 0075 as implemented.
CHANGELOG.md Documents proposal 0075 feature and spec pin advance.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/openarmature/graph/parallel_branches.py
Comment thread src/openarmature/graph/parallel_branches.py
Comment thread src/openarmature/graph/parallel_branches.py
Comment thread src/openarmature/graph/parallel_branches.py
The callable-branch started/completed pair hard-coded attempt_index=0.
Under node-level retry that mis-reports the graph-engine §6 keying tuple
and the Langfuse observation's metadata.attempt_index on the second and
later attempts. Read current_attempt_index() once at dispatch entry,
before the branch chain (which may run its own retries) so the pair
shares one value, and thread it through all three dispatches.
@chris-colinsky chris-colinsky merged commit c5e09a6 into main Jun 21, 2026
6 checks passed
@chris-colinsky chris-colinsky deleted the feature/0075-parallel-branches-callable-branches branch June 21, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants